package com.invest.infra.excel.upload;

import com.google.common.collect.Lists;
import com.invest.config.AppProperties;
import com.invest.infra.excel.upload.vo.ExcelTemplate;
import com.invest.infra.excel.upload.vo.ExcelVO;
import com.invest.infra.excel.upload.vo.RowErrorResult;
import com.invest.infra.excel.upload.vo.SheetErrorResult;
import com.invest.infra.excel.utils.annotation.ExcelColumnAnnotation;
import com.invest.infra.excel.utils.annotation.ExcelColumnAnnotationUtills;
import com.invest.infra.excel.utils.annotation.ExcelFieldUtils;
import com.invest.infra.excel.utils.annotation.ExcelSheetAnnotation;
import com.invest.infra.excel.wrapper.*;
import com.invest.utils.io.FileIOUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Created by evan on 2016/7/12.
 */
@Service
public class ExcelService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelService.class);
    @Autowired
    private ExcelValidateService excelValidateService;
    @Autowired
    private AppProperties appProperties;

    /**
     *
     * @param out 输出excel
     * @param exportName 输出excel名称
     * @param tmpFullPath 模板路径
     * @param data  //模板数据
     * @param validateMap //excel约束 key:beanFieldName value:约束值
     */
    public void export(OutputStream out,String exportName,String tmpFullPath,List<?> data,Map<String,String[]> validateMap){

        ExWorkbook workbook = null;
        try {
            Assert.notNull(out);
            Assert.notNull(exportName);
            Assert.notNull(tmpFullPath);
            Assert.notEmpty(data);
            InputStream in = getClass().getClassLoader().getResourceAsStream(tmpFullPath);
            Assert.notNull(in,"导出模板不存在");
            //提取注解
            ExcelSheetAnnotation clsAnnotation = ExcelFieldUtils.getClassAnnotation(data.get(0).getClass());
            Map<String, ExcelColumnAnnotation> fieldAnnotation = ExcelFieldUtils.getFieldAnnotation(data.get(0).getClass());
            Assert.notNull(clsAnnotation,"数据对象不正确，缺少ExcelSheetAnnotation");
            Assert.notNull(fieldAnnotation,"数据对象不正确，缺少ExcelColumnAnnotation");

             workbook = ExWorkbookFactory.instance(in, exportName);

             CellStyle cellStyle = ExCellStyleUtil.defaultStyle(workbook.getPoiWorkbook());
             ExSheet exSheet = workbook.getSheets().get(clsAnnotation.name());

                //输出数据
            for (int i = 0; i < data.size(); i++) {
                Row poiRow = exSheet.createPoiRow(i + clsAnnotation.dataStartRow());
                Object dataItem = data.get(i);
                for (ExcelColumnAnnotation columnAnn : fieldAnnotation.values()) {
                    Field filed = ExcelFieldUtils.getFiled(dataItem.getClass(), columnAnn.beanFieldName());
                    Object filedValue = ExcelFieldUtils.getFiledValue(filed, dataItem);
                    if (filedValue instanceof String) {
                        Cell cell = poiRow.createCell(columnAnn.index());
                        cell.setCellStyle(cellStyle);
                        cell.setCellValue(new HSSFRichTextString((String) filedValue));
                    }
                }
            }
            if(MapUtils.isNotEmpty(validateMap)){
                for(String beanFieldName: validateMap.keySet()){
                    ExcelColumnAnnotation annotation = fieldAnnotation.get(beanFieldName);
                    exSheet.addValidation(validateMap.get(beanFieldName),clsAnnotation.dataStartRow(),annotation.index());
                }
            }
            workbook.write(out);
        }finally {
            if(workbook != null) {
                workbook.close();
            }
            FileIOUtils.close(out);
        }

    }

    /**
     *
     * @return
     */
    public SheetErrorResult execute(ExcelVO excelVO,ExcelDataProcessor processor){

        ExWorkbook workbook = null;

        try {
            Assert.notNull(excelVO.getInputStream(), "excel不能为空");
            Assert.notNull(excelVO.getFileName(), "文件名不能为空");
            Assert.notNull(excelVO.getType(), "接受不能为空");
            workbook = ExWorkbookFactory.instance(excelVO.getInputStream(), excelVO.getFileName());
            ExcelSheetAnnotation clsAnnotation = ExcelFieldUtils.getClassAnnotation(excelVO.getType());

            SheetErrorResult sheetErrorResult = excelValidateService.validateSheet(workbook, clsAnnotation);
            if (StringUtils.isNotBlank(sheetErrorResult.getErrorMessage())) {
                return sheetErrorResult;
            }

            Map<String, ExcelColumnAnnotation> fieldAnnotation = ExcelFieldUtils.getFieldAnnotation(excelVO.getType());
            ExSheet exSheet = workbook.getSheets().get(sheetErrorResult.getSheetName());

            List<Integer> successIndexes = Lists.newArrayList();
            List<ExcelTemplate> templateData = Lists.newArrayList();

            for (int i = clsAnnotation.dataStartRow(); i < exSheet.getRowCount(); i++) {

                ExCell[] cells = exSheet.getCells(i);
                RowErrorResult rowErrorResult = excelValidateService.validateRow(i, cells, fieldAnnotation.values(),
                        excelVO.getParamMap());
                // 保存错误信息
                if (rowErrorResult.hasError()) {
                    sheetErrorResult.getErrorResults().add(rowErrorResult);
                    sheetErrorResult.addFail();
                    continue;
                }
                // 有数据
                if (MapUtils.isNotEmpty(rowErrorResult.getKeyData())) {
                    templateData.add(createBean(excelVO.getType(), fieldAnnotation.values(), cells));
                    sheetErrorResult.addSuccess();
                }
                successIndexes.add(i);
            }
            processExcelError(sheetErrorResult, exSheet, excelVO, successIndexes);
            if (processor != null) {
                processor.process(templateData, excelVO.getParamMap());
            }
            return sheetErrorResult;
        }finally {
            if(workbook != null) {
                workbook.close();
            }
        }
    }

    // 生成错误的excel表格 提供给客户观看
    private void processExcelError(SheetErrorResult sheetErrorResult,ExSheet exSheet,ExcelVO excelVO,List<Integer> successIndexes){
        //没有错误不处理
        if(CollectionUtils.isEmpty(sheetErrorResult.getErrorResults())){
            return ;
        }
        //删除正确的行数 从后往前删除
        if(CollectionUtils.isNotEmpty(successIndexes)){
            for(int i = successIndexes.size()-1;i>=0;i--){
                exSheet.shiftRow(successIndexes.get(i));
            }
        }

        //生成错误excel
        String errorFilePath = sheetErrorResult.buildErrorFileUri(appProperties, excelVO);
        OutputStream outputStream = null;
        try {
             outputStream = FileIOUtils.fileOutputStream(errorFilePath);
            exSheet.getWorkbook().write(outputStream);
        }finally {
            FileIOUtils.close(outputStream);
        }

    }

    private ExcelTemplate createBean(Class<? extends ExcelTemplate> type,
                          Collection<ExcelColumnAnnotation> exAnnotation,
                          ExCell[] cells){

        try {
            //正确保存到数据库库
            ExcelTemplate bean = (ExcelTemplate) type.newInstance();
            for(int j=0;j< cells.length ; j++) {

                ExcelColumnAnnotation annotation = ExcelColumnAnnotationUtills.getByIndex(exAnnotation,j);
                if(annotation != null) {
                    Field filed = ExcelFieldUtils.getFiled(type, annotation.beanFieldName());
                    ExcelFieldUtils.writeFiled(filed, bean, cells[j].getText());
                }
            }
            return bean;
        }catch (Exception ex){
            LOGGER.error("保存EXCEL数据异常",ex);
            throw  new RuntimeException(ex);
        }
    }
}
